#ifndef _POLL_SVR_H_
#define _POLL_SVR_H_

#include <iostream>
#include <poll.h>
#include "Sock.hpp"
#include "log.hpp"
#include <sys/time.h>
#include <string>
#include <vector>

#define FD_NONE -1

class PollServer
{
public:
    static const int nfds = 100; // 设定可操作的最大文件描述符个数
public:
    PollServer(const uint16_t &port = 8080)
        : _port(port), _nfds(nfds)
    {
        // 1. 创建套接字
        _listensock = Sock::Socket();

        // 2. 绑定
        Sock::Bind(_listensock, _port);

        // 3. 监听
        Sock::Listen(_listensock);

        logMessage(DEBUG, "Create base select success.");

        _pfds = new struct pollfd[_nfds];

        // 初始化结构体
        for (int i = 0; i < _nfds; i++)
        {
            _pfds[i].fd = FD_NONE;
            _pfds[i].events = _pfds->revents = 0;
        }
        // 指定第一个用于检测 _listensock！
        _pfds[0].fd = _listensock;
        _pfds[0].events = POLLIN;
        _timeout = 1000;
    }

    void Start()
    {

        while (true)
        {
            // 设置poll检测！
            int n = poll(_pfds, _nfds, _timeout);
            switch (n)
            {
            case 0: // 没有就绪资源（后续可以补充其他功能操作）
                logMessage(DEBUG, "time out ...");
                break;
            case -1:
                logMessage(WARNING, "select error ：%d : %s", errno, strerror(errno));
                break;
            default:
                // 到此说明获取到了可用连接！
                logMessage(DEBUG, "Get a new link event ...");
                HandlerEvent();
                break;
            }
        }
    }

    ~PollServer()
    {
        if (_listensock >= 0)
            close(_listensock);
        if (_pfds)
            delete[] _pfds;
    }

private:
    void HandlerEvent()
    {
        // 遍历我们的 sock 管理库
        for (int i = 0; i < _nfds; i++)
        {
            // 1. 不操作非法的文件描述符
            if (_pfds[i].fd == FD_NONE)
                continue;
            // 2. 判断合法的 sock 中的就绪情况！
            if (_pfds[i].revents & POLLIN)
            {
                // 到此位置必然就绪！
                // 分流：区分是连接获取事件就绪 还是其他资源就绪！
                if (_pfds[i].fd == _listensock)
                    Accepter();
                else
                    Recver(i);
            }
        }
    }

    void Accepter()
    {
        std::string clientip;
        uint16_t clientport = 0;
        // listensock上面的读事件就绪了，表示可以读取了
        // 获取新连接了
        int sock = Sock::Accept(_listensock, &clientip, &clientport); // 这里在进行accept会不会阻塞？不会！
        if (sock < 0)
        {
            logMessage(WARNING, "accept error");
            return;
        }
        logMessage(DEBUG, "get a new line success : [%s:%d] : %d", clientip.c_str(), clientport, sock);
        int pos = 1;
        for (; pos < _nfds; pos++)
        {
            if (_pfds[pos].fd == FD_NONE)
                break;
        }
        if (pos == _nfds)
        {
            // 对struct pollfd进行自动扩容
            logMessage(WARNING, "%s:%d", "poll server already full，close: %d", sock);
            close(sock);
        }
        else
        {
            _pfds[pos].fd = sock;
            _pfds[pos].events = POLLIN;
        }
    }



    void Recver(int pos)
    {
        // 读事件就绪：INPUT事件到来、recv，read
        logMessage(DEBUG, "message in, get IO event: %d", _pfds[pos]);
        // 暂时先不做封装, 此时select已经帮我们进行了事件检测，fd上的数据一定是就绪的，即 本次 不会被阻塞

        // 这样读取有bug吗？有的，你怎么保证以读到了一个完整包文呢？

        char buffer[1024];
        int n = recv(_pfds[pos].fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            logMessage(DEBUG, "client[%d]# %s", _pfds[pos].fd, buffer);
        }
        else if (n == 0)
        {
            logMessage(DEBUG, "client[%d] quit, me too...", _pfds[pos].fd);
            // 1. 我们也要关闭不需要的fd
            close(_pfds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
            _pfds[pos].fd = FD_NONE;
            _pfds[pos].events = 0;
        }
        else
        {
            logMessage(WARNING, "%d sock recv error, %d : %s", _pfds[pos].fd, errno, strerror(errno));
            // 1. 我们也要关闭不需要的fd
            close(_pfds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
            _pfds[pos].fd = FD_NONE;
            _pfds[pos].events - 0;
        }
    }

    void DebugPrint()
    {
        std::cout << "_pfds[].fd: ";
        for (int i = 0; i < _nfds; i++)
        {
            if (_pfds[i].fd == FD_NONE)
                continue;
            std::cout << _pfds[i].fd << " ";
        }
        std::cout << std::endl;
    }

private:
    // 作为服务器要有端口号
    uint16_t _port;
    int _listensock;

    // pollfd
    struct pollfd *_pfds;
    int _nfds;
    int _timeout;
};

#endif